package com.jackstraw.jack_base.datasource.jpa.base.service.impl;

import cn.hutool.core.bean.copier.CopyOptions;
import com.jackstraw.jack_base.datasource.database.parten.base.entity.page.PageData;
import com.jackstraw.jack_base.datasource.jpa.base.dao.BaseDao;
import com.jackstraw.jack_base.datasource.jpa.base.dao.database.JpaDao;
import com.jackstraw.jack_base.datasource.jpa.base.entity.Query;
import com.jackstraw.jack_base.datasource.jpa.base.entity.po.JpaPo;
import com.jackstraw.jack_base.datasource.jpa.base.service.JpaService;
import com.jackstraw.jack_base.util.BeanUtil;
import com.jackstraw.jack_base.util.CollectionUtil;
import com.jackstraw.jack_base.util.ObjectUtil;
import com.jackstraw.jack_base.util.StringUtil;
import com.jackstraw.jack_base.util.entity.DefaultConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;


@Slf4j
@Transactional
public class JpaServiceImpl<DAO extends JpaDao<PO, ID>,
        PO extends JpaPo<PO, ID>, BO extends PO, ID>
        implements JpaService<DAO, PO, BO, ID> {


    /**
     * 数据持久层
     */
    @Autowired
    protected DAO dao;

    /**
     * 和dao非共用同一个EntityManager，所以两者事务不共享
     */
    @Autowired
    private BaseDao baseDao;

    /**
     * 实体类Class
     */
    private final Class<PO> poClass = currentPOClass();

    /**
     * 数据持久层Class
     */
    private final Class<DAO> daoClass = currentDAOClass();

    /**
     * 业务操作类Class
     */
    private final Class<BO> boClass = currentBOClass();

    /**
     * 主键类型
     */
    private final Class<ID> idClass = currentIDClass();


    @Override
    public DAO getDao() {
        return this.dao;
    }

    @Override
    public BaseDao getBaseDao() {
        return baseDao;
    }

    @Override
    public Class<PO> getPoClass() {
        return this.poClass;
    }

    @Override
    public Class<BO> getBoClass() {
        return this.boClass;
    }

    @Override
    public Class<DAO> getDaoClass() {
        return this.daoClass;
    }

    @Override
    public Class<ID> getIdClass() {
        return this.idClass;
    }


    @Override
    public Query<PO> query(Map<String, Object> params) {

        Query<PO> query = Query.forClass(this.getDao().entityManager(), this.getPoClass());

        if (CollectionUtil.MapUtil.isEmpty(params)) {
            return query;
        }

        //分页参数
        if (params.get(DefaultConstants.PAGE_NUM) != null) {
            Integer pageNum = Integer.valueOf((Integer) params.get(DefaultConstants.PAGE_NUM));
            query.setPageNum(pageNum);
        }
        if (params.get(DefaultConstants.PAGE_SIZE) != null) {
            Integer pageSize = Integer.valueOf((Integer) params.get(DefaultConstants.PAGE_SIZE));
            query.setPageSize(pageSize);
        }

        String orderField = null;
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(params.get(DefaultConstants.ORDER_FIELD))) {
            orderField = (String) params.get(DefaultConstants.ORDER_FIELD);
        }

        Map<String, String> orderMap = null;
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(params.get(DefaultConstants.ORDER))) {
            orderMap = (Map<String, String>) params.get(DefaultConstants.ORDER);
        }

        if (StringUtil.StringUtilsBylang3.isNotEmpty(orderField)) {
            if (CollectionUtil.MapUtil.isEmpty(orderMap)) {
                orderMap = new HashMap<>();
            }
            Map<String, String> finalOrderMap = orderMap;
            List<String> orderFieldList = Arrays.asList(orderField.split(","));

            orderFieldList.stream().forEach(field -> {
                String order = finalOrderMap.get(field);
                query.addOrder(field, order);
            });
        }

        // TODO 条件未设置,需要重写该方法进行条件设置
        criteriaQuery(params, query);

        return query;
    }

    @Override
    public Query<PO> criteriaQuery(Map<String, Object> params, Query<PO> query) {
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(query)) {
            query = Query.forClass(this.getDao().entityManager(), this.getPoClass());
        }

        if (CollectionUtil.MapUtil.isEmpty(params)) {
            return query;
        }

        String idFieldName = idFieldName();
        if (StringUtil.StringUtilsBylang3.isEmpty(idFieldName)) {
            return query;
        }
        Object keyValue = params.get(idFieldName);
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(keyValue)) {
            query.eq(idFieldName, keyValue);
        }
        return query;
    }


    @Override
    public PageData<BO> page(Map<String, Object> params) {
        Query<PO> query = query(params);
        Long count = this.getDao().countByQuery(query);
        List<BO> boList = toBo(this.getDao().query(query));
        PageData<BO> pageData = new PageData<>(boList, count);
        pageData.setPageNum(query.getPageNum());
        pageData.setPageSize(query.getPageSize());
        return pageData;
    }

    @Override
    public List<BO> list(Map<String, Object> params) {
        Query<PO> query = criteriaQuery(params, null);
        return toBo(this.getDao().query(query));
    }

    @Override
    public BO get(ID id) {
        Optional<PO> optionalPO = this.getDao().findById(id);
        if (optionalPO.isPresent()) {
            return toBo(optionalPO.get());
        }
        return null;
    }

    @Override
    public List<BO> get(Collection<ID> idS) {
        return toBo(this.getDao().findAllById(idS));
    }

    @Override
    public void insert(BO bo) {
        PO po = this.getDao().saveAndFlush(toPo(bo));
        BeanUtil.BeanUtilByHutool.copyProperties(po, bo, CopyOptions.create().ignoreNullValue());
    }

    @Override
    public List<BO> insertBatch(List<BO> boS) {
        return toBo(this.getDao().saveAllAndFlush(toPo(boS)));
    }

    @Override
    public void update(BO bo) {
        this.getDao().update(toPo(bo));
    }

    @Override
    public void updateBatch(List<BO> boS) {
        this.getDao().updateAll(toPo(boS));
    }

    @Override
    public void delete(ID id) {
        this.getDao().deleteById(id);
    }

    @Override
    public void deleteBatch(List<ID> idS) {
        this.getDao().deleteAllById(idS);
    }
}
